package org.robert.export_client.easyexcel.write.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.robert.common.commonutil.exception.AppException;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.robert.export_client.aop.ExcelDictAdapter;
import org.robert.export_client.config.executor.ExportExecutorFactory;
import org.robert.export_client.config.executor.ExportThreadPoolExecutor;
import org.robert.export_client.easyexcel.write.ExcelBaseQuery;
import org.robert.export_client.easyexcel.write.IExcelWriteService;
import org.robert.export_client.easyexcel.write.entity.*;
import org.robert.export_client.easyexcel.entity.WriteRequest;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @Description 写入磁盘，构造方法只提供必填的，
 * @Author robert
 * @Date 2024/1/14 0:13
 **/
@Slf4j
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class ExcelWriteSourceService<T> extends WriteRequest<T> implements IExcelWriteService {

    public static final ExportThreadPoolExecutor excelExportThreadPool
            = new ExportThreadPoolExecutor(1, 4, 60,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(200),
            new ExportExecutorFactory("excelExportSource"));


    private ExcelBaseQuery<T> repository;

    private T t;

    private Integer limit = 2000;


    public ExcelWriteSourceService(String path, ExcelBaseQuery<T> repository, Class<T> clazz) {
        this.path = path;
        this.repository = repository;
        this.clazz = clazz;
    }

    public ExcelWriteSourceService(String path, ExcelBaseQuery<T> repository, Class<T> clazz,
                                   Map<String, Map<String, String>> dictMap){
        this.path = path;
        this.repository = repository;
        this.dictMap = dictMap;
        this.clazz=clazz;
        if(org.apache.commons.collections4.MapUtils.isNotEmpty(dictMap)){
            this.checker = new ExcelDictAdapter<T>();
        }
    }

    public ExcelWriteSourceService(String path, ExcelBaseQuery<T> repository, Class<T> clazz,
                                   Map<String, Map<String, String>> dictMap, T t){
        this.path = path;
        this.repository = repository;
        this.dictMap = dictMap;
        this.clazz=clazz;
        this.t = t;
        if(org.apache.commons.collections4.MapUtils.isNotEmpty(dictMap)){
            this.checker = new ExcelDictAdapter<T>();
        }
    }

    public ExcelWriteSourceService(String path, ExcelBaseQuery<T> repository, Class<T> clazz,
                                   Map<String, Map<String, String>> dictMap, T t, String fileName, String sheetName){
        this.path = path;
        this.repository = repository;
        this.dictMap = dictMap;
        this.clazz=clazz;
        this.t = t;
        this.fileName = fileName;
        this.sheetName = sheetName;
        if(org.apache.commons.collections4.MapUtils.isNotEmpty(dictMap)){
            this.checker = new ExcelDictAdapter<T>();
        }
    }

    private void execute(){
        try {
            Integer size = this.limit;
            if (size == null || size == 0) {
                size = 5000;
            }
            long start = System.currentTimeMillis();
            Long aLong = repository.queryCount(t);
            if (aLong == null || aLong == 0) {
                throw new AppException("500", "没有数据");
            }
            int pageCount = (int) Math.ceil((double) aLong / size);
            String path = this.path + fileName + ".xlsx";
            try (ExcelWriter excelWriter = EasyExcel.write(path, clazz).build()) {
                // 这里注意 如果同一个sheet只要创建一次
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
                List<T> data = new ArrayList<>();
                // 去调用写入,使用时根据数据库分页的总的页数来
                for (int page = 1; page <= pageCount; page++) {
                    int finalPage = (page - 1) * size;
                    // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                    data = repository.query(t, finalPage, size);
                    // 字典转换
                    if (org.apache.commons.collections4.MapUtils.isNotEmpty(dictMap) && CollectionUtils.isNotEmpty(data)) {
                        ExcelDictAdapter<T> checker = new ExcelDictAdapter<>();
                        checker.setDict(data, dictMap);
                    }
                    excelWriter.write(data, writeSheet);
                    data.clear();
                }
            }
            long endTime = System.currentTimeMillis() - start;
            log.info("execute run time: {} ms", endTime);
        }catch (Exception e){
            log.error(e.getMessage(), e);
        }
    }


    @Override
    public ExcelResponse exportToExcel(boolean isAsync) throws Exception {
        if(!check() || repository == null || limit == 0){
            return new ExcelResponse("error", "参数异常");
        }
        Long taskWaitTime = 0L;
        if(isAsync){
            Runnable exportTask = () -> execute();
            // 提交任务到线程池
            excelExportThreadPool.execute(exportTask);
            taskWaitTime = excelExportThreadPool.getTaskWaitTime(exportTask, 15 * 1000);
            log.info("Task: {}, wait time: {}", Thread.currentThread().getName(), taskWaitTime);
        }else{
            execute();
        }
        return new ExcelResponse(taskWaitTime);
    }
}
